package com.ybear.ybcomponent.widget.ribbon;

import android.animation.Animator;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import android.widget.FrameLayout;

import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.UiThread;

import com.ybear.ybcomponent.base.adapter.IItemData;

import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;

/**
 彩带控件
 Item从一侧进入，展示，离开等一系列动画展示
 */
public class EnterRibbonView extends FrameLayout {
    private final String TAG = getClass().getSimpleName();
    private transient final List<ViewHolder> mRibbonQueueDataPool = new CopyOnWriteArrayList<>();
    //    //是否处于播放动画状态
//    private boolean isPlaying = false;
    //是否处于释放资源状态，当为true时，停止队列播放
    private boolean isRelease = false;
    private boolean isEnableLog = false;
    private long mEnterMillisecond = 300;                       //动画的进入时长
    private long mWaitMillisecond = 2000;                       //动画的持续时长
    private long mExitMillisecond = 300;                        //动画的退出时长
    private long mAnimationEnterDelayedTime = 0;                  //进入的延迟时间
    private long mAnimationExitDelayedTime = 0;                   //退出的延迟时间
    private int mMaxLine = 1;                                   //最大展示列数, 默认列数为1
    private int mMultiLineDirection = -1;                       //多列时的展示方向，1：从上往下，-1：从下往上
    private float mItemOffsetX = 0;                             //Item的偏移X轴
    private int mEnterAnimIdleIndexReCount = 0;                 //开始进入动画处理失败时重试次数
    private OnViewAnimationListener mOnViewAnimationListener;   //动画监听器

    //下一条列的下标
    private final AtomicInteger mAtoNextLineIndex = new AtomicInteger( -1 );
    //空闲列的数量
    private final AtomicInteger mAtoIdleLineCount = new AtomicInteger( mMaxLine );

    private Adapter<? extends BaseItemData, ? extends ViewHolder> mAdapter;
    private OnRibbonCallable<EnterRibbonView, Boolean> mOnPlayStartCall;
    private OnRibbonCallable<EnterRibbonView, Boolean> mOnPlayEndCall;

    private ViewHolder mCurrentHolder = null;
    private BaseItemData mCurrentData = null;

    public EnterRibbonView(@NonNull Context context) { this( context, null ); }

    public EnterRibbonView(@NonNull Context context, @Nullable AttributeSet attrs) {
        this( context, attrs, 0 );
    }

    public EnterRibbonView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super( context, attrs, defStyleAttr );
        init();
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public EnterRibbonView(@NonNull Context context, @Nullable AttributeSet attrs,
                           int defStyleAttr, int defStyleRes) {
        super( context, attrs, defStyleAttr, defStyleRes );
        init();
    }

    private void init() {
        setClipChildren( false );
        if( getRootView() instanceof ViewGroup ) {
            ((ViewGroup)getRootView()).setClipChildren( false );
        }
    }

    public void setEnableLog(boolean enableLog) {
        isEnableLog = enableLog;
    }

    /**
     设置数据适配器
     @param adapter     数据适配器
     */
    public void setAdapter(@NonNull Adapter<? extends BaseItemData, ? extends ViewHolder> adapter) {
        mAdapter = adapter;
        //通知参数发生改变
        notifyParamSetChanged();
    }

    /**
     获取设置的适配器
     @return            数据适配器
     */
    public Adapter<? extends BaseItemData, ? extends ViewHolder> getAdapter() { return mAdapter; }

    /**
     播放结束事件监听器
     @param call        回调
     */
    public void setOnPlayEndListener(OnRibbonCallable<EnterRibbonView, Boolean> call) {
        mOnPlayEndCall = call;
    }

    /**
     通知参数发生改变
     */
    private void notifyParamSetChanged() {
        Adapter<? extends BaseItemData, ? extends ViewHolder> adapter = getAdapter();
        if( adapter == null ) return;
        adapter.setEnterRibbonView( this );
        if( mMaxLine <= 0 ) mMaxLine = 1;
        String adapterName = adapter.getClass().getSimpleName();
        int viewTypeCount = adapter.onViewTypeCount();
        if( viewTypeCount == 0 ) throw new RuntimeException( "[onViewTypeCount()] View type must be > 0." );
        //最大缓存数。两倍行数 × 类型总数
        int maxSize = ( mMaxLine * 2 ) * viewTypeCount;
        //总缓存数
        int queueSize = mRibbonQueueDataPool.size();
        //不需要初始化
        if( queueSize == maxSize ) return;
        //初始化 或者 maxLine发生了改变，缓存不足的情况
        if( queueSize == 0 || queueSize < maxSize ) {
            for( int i = 0; i < Math.abs( queueSize == 0 ? maxSize : queueSize - maxSize ); i++ ) {
                int viewTypeIndex = i % viewTypeCount;
                int itemViewType = adapter.getItemViewType( i, viewTypeIndex );
                //实例一个Holder
                ViewHolder holder = adapter.createViewHolder( this, itemViewType );
                //初始化为空闲状态
                updateIdleState( holder, -1 );
                mRibbonQueueDataPool.add( holder );
                View itemView = holder.getItemView();
                itemView.setVisibility( INVISIBLE );
                addView( itemView );
                log( "initCheckQueue -> init[%s] -> " +
                                "cacheIndex:%s, viewTypeIndex:%s, itemViewType:%s, queueSize:%s, maxSize:%s",
                        adapterName, i, viewTypeIndex, itemViewType, queueSize, maxSize
                );
            }
            log( "initCheckQueue -> init[%s] -> cacheSize:%s",
                    adapterName, mRibbonQueueDataPool.size()
            );
            return;
        }
        //maxLine发生了改变，缓存太多的情况
        for( int i = queueSize - maxSize - 1; i >= 0; i-- ) {
            //移除多余的队列数据
            ViewHolder vh = mRibbonQueueDataPool.remove( i );
            //释放单个缓存的队列数据
            if( vh != null && vh.isIdle() ) vh.onReleaseHolder( this );
        }
        log( "initCheckQueue -> surplus:%s", queueSize - maxSize );
    }
    
    private void log(String msg, Object... args) {
        if( !isEnableLog ) return;
        Log.d( TAG, args == null ? msg : String.format( msg, args ) );
    }

    /**
     释放资源
     */
    public void release() {
        isRelease = true;
        post( () -> {
            for( ViewHolder vh : mRibbonQueueDataPool ) {
                if( vh != null ) vh.onReleaseHolder( this );
            }
            mRibbonQueueDataPool.clear();
            getAdapter().onRelease();
            log( "release" );
        } );
    }

    /**
     最大展示View的行数
     @param maxLine        最大行数
     @return               this
     */
    public EnterRibbonView setMaxLine(int maxLine) {
        mAtoIdleLineCount.set( mMaxLine = maxLine );
        //通知参数发生改变
        notifyParamSetChanged();
        return this;
    }

    /**
     多列时的展示方向
     @param direction       1：从上往下，-1：从下往上
     @return                this
     */
    public EnterRibbonView setMultiLineDirection(int direction) {
        mMultiLineDirection = direction;
        return this;
    }
    /**
     多列时从上往下展示
     @return                this
     */
    public EnterRibbonView setMultiLineTopToDown() {
        return setMultiLineDirection( 1 );
    }
    /**
     多列时从下往上展示
     @return                this
     */
    public EnterRibbonView setMultiLineDownToTop() {
        return setMultiLineDirection( -1 );
    }

    /**
     Item的偏移X轴
     @param offsetX     偏移的X轴
     */
    public void setItemOffsetX(float offsetX) {
        mItemOffsetX = offsetX;
    }

    /**
     设置动画时间
     @param enterMillisecond    动画的进入时长
     @param waitMillisecond     动画的持续时长
     @param exitMillisecond     动画的退出时长
     */
    public void setAnimationTime(long enterMillisecond,
                                 long waitMillisecond,
                                 long exitMillisecond) {
        setAnimationEnterTime( enterMillisecond );
        setAnimationWaitTime( waitMillisecond );
        setAnimationExitTime( exitMillisecond );
    }
    /**
     动画的进入时长
     @param millisecond     毫秒
     */
    public void setAnimationEnterTime(long millisecond) {
        mEnterMillisecond = millisecond;
    }
    /**
     动画的持续时长
     @param millisecond     毫秒
     */
    public void setAnimationWaitTime(long millisecond) {
        mWaitMillisecond = millisecond;
    }
    /**
     动画的退出时长
     @param millisecond     毫秒
     */
    public void setAnimationExitTime(long millisecond) {
        mExitMillisecond = millisecond;
    }

    /**
     设置动画进入/退出时的延迟时间
     @param enterDelayedMillisecond    动画进入时的延迟时间
     @param exitDelayedMillisecond     动画退出时的延迟时间
     */
    public void setDelayedMillisecond(long enterDelayedMillisecond, long exitDelayedMillisecond) {
        setAnimationEnterDelayedTime( enterDelayedMillisecond );
        setAnimationExitDelayedTime( exitDelayedMillisecond );
    }
    /**
     * 设置动画进入时的延迟时间
     * @param delayedMillisecond    动画的进入延迟时长
     */
    public void setAnimationEnterDelayedTime(long delayedMillisecond) {
        mAnimationEnterDelayedTime = delayedMillisecond;
    }
    /**
     * 设置动画退出时的延迟时间
     * @param delayedMillisecond    动画的退出延迟时长
     */
    public void setAnimationExitDelayedTime(long delayedMillisecond) {
        mAnimationExitDelayedTime = delayedMillisecond;
    }
    
    /**
     动画监听器
     @param listener        监听器
     */
    public void setOnViewAnimationListener(OnViewAnimationListener listener) {
        mOnViewAnimationListener = listener;
    }

    public int getCurrentIdleLineCount() { return mAtoIdleLineCount.get(); }

    /**
     * 立即停止当前动画
     */
    public synchronized void nowExitAnim(long delayMillis) {
        Runnable r = () -> {
            try {
                ViewHolder holder = getCurrentHolder();
                BaseItemData data = getCurrentData();
                if( holder == null || data == null ) return;
                /* 停止进入动画 */
                ValueAnimator vaEnter = holder.getEnterValAnim();
                if( vaEnter != null && vaEnter.isRunning() ) vaEnter.end();
                /* 移除自旋动画 */
                RunSpinExitAnim anim = findRunSpinExitAnim( data.getRibbonMsgId() );
                if( anim != null ) {
                    //移除自旋
                    anim.removeSpin();
                    //退出动画
                    exitAnim( anim );
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        };
        if( delayMillis <= 0 ) {
            r.run();
            return;
        }
        postDelayed( r, delayMillis );
    }

    /**
     * 立即停止当前动画
     */
    public synchronized void nowExitAnim() { nowExitAnim( 0 ); }

    @Nullable
    public ViewHolder getCurrentHolder() { return mCurrentHolder; }

    @Nullable
    public BaseItemData getCurrentData() { return mCurrentData; }

    @UiThread
    private void notifyNext() {
        if( isRelease ) return;
        int idleLineCount = getCurrentIdleLineCount();
        log( "notifyNext -> " + "idleLineCount:%s", idleLineCount );
        if( /*isPlaying || */idleLineCount <= 0 ) return;
//        isPlaying = true;
        if( getVisibility() != VISIBLE ) setVisibility( VISIBLE );
        nextQueueAnim( false );
    }

    @UiThread
    private void nextQueueAnim(boolean fromIdle) {
        Adapter<? extends BaseItemData, ? extends ViewHolder> adapter = getAdapter();
        if( adapter == null || isRelease ) return;
        BaseItemData data = adapter.poll();
        int queueSize = adapter.getItemCount();
        //队列播放完毕
        if( data == null ) {
            int idleLineCount = getCurrentIdleLineCount();
            //动画队列还没播放完
            if( idleLineCount != mMaxLine ) return;
            if( mOnPlayEndCall != null ) mOnPlayEndCall.call( this, fromIdle );
            log( "nextQueueAnim -> Queue anim end." );
//            isPlaying = false;
            //退出动画结束后
            mAtoNextLineIndex.set( -1 );
            log( "nextQueueAnim -> Queue anim end. -> " +
                            "idleLineCount:%s, nextLineIndex:%s",
                    getCurrentIdleLineCount(), mAtoNextLineIndex.get()
            );
            return;
        }
        mAtoNextLineIndex.incrementAndGet();
        //占用一个空闲行数
        if( getCurrentIdleLineCount() > 0 ) mAtoIdleLineCount.decrementAndGet();
        //开始动画
        enterAnim( data );
        //空闲列的数量
        int idleLineCount = getCurrentIdleLineCount();
        log( "nextQueueAnim -> " +
                        "fromIdle:%s, idleLineCount:%s, nextLineIndex:%s, queueSize:%s, data:%s",
                fromIdle, idleLineCount, mAtoNextLineIndex.get(), queueSize, data
        );
        //检查是否还有空闲的位置，有的话继续调用
        if( fromIdle || idleLineCount == 0 || queueSize == 0 ) return;
        for( int i = 0; i < Math.min( idleLineCount, queueSize ); i++ ) {
            nextQueueAnim( true );
        }
    }

    /**
     获取初始化的动画配置
     @param holder  ViewHolder
     @return        动画配置
     */
    @NonNull
    private CreateQueue initAnimation(@NonNull ViewHolder holder, @NonNull BaseItemData data, boolean isEnter) {
        //获取初始化配置
        CreateQueue animInit = initAnimation( holder, holder, data , isEnter );
        if( animInit == null ) {
            throw new NullPointerException(String.format(
                    "The CreateQueue return by %s is null.",
                    isEnter ? "onEnterCreateQueue" : "onExitCreateQueue"
            ));
        }
        CreateQueue callAnimInit = initAnimation( mOnViewAnimationListener, holder, data , isEnter );
        if( callAnimInit != null ) animInit = callAnimInit;
        return animInit;
    }

    private CreateQueue initAnimation(OnViewAnimationListener listener,
                                      @NonNull ViewHolder holder,
                                      @NonNull BaseItemData data,
                                      boolean isEnter) {
        if( listener == null ) return null;
        if( isEnter ) {
            return listener.onEnterCreateQueue( holder, data, mItemOffsetX, mMultiLineDirection );
        }
        return listener.onExitCreateQueue( holder, data, mItemOffsetX, mMultiLineDirection );
    }

    @UiThread
    private void enterAnim(@NonNull BaseItemData data) {
        if( isRelease ) return;
        //空闲的下标
        int idleIndex = getIdleIndex( data.onItemViewType() );
        //检查是否需要重试
        if( checkReCountEnterAnim( data, idleIndex ) ) return;
        //获取空闲下标中的Holder
        ViewHolder holder = getIdleHolder( data, idleIndex );
        //占用状态
        updateIdleState( holder, idleIndex );
        //获取初始化的动画配置
        CreateQueue animInit = initAnimation( holder, data, true );
        holder.getItemView().setVisibility( View.VISIBLE );
        //开始动画
        ValueAnimator valAnim = updateAnim(
                1,
                holder.getEnterValAnim(),
                animInit.getValues(),
                mEnterMillisecond,
                animInit.getTimeInterpolator(),
                ( view, time ) -> {
                    holder.onEnterAnimationUpdate( holder, data, time );
                    if( mOnViewAnimationListener != null ) {
                        mOnViewAnimationListener.onEnterAnimationUpdate( holder, data, time );
                    }},
                ( view, anim ) -> {
                    //处理结束动画
                    checkExitAnim( holder, data, mWaitMillisecond );
                    log( "ribbonAnim -> enterAnim end" );
                } );
        holder.setEnterValAnim( valAnim );
        mCurrentHolder = holder;
        mCurrentData = data;
        log(
                "ribbonAnim -> enterAnim -> idleIndex:%s, data:%s, holder:%s",
                idleIndex, data, holder
        );
    }

    /**
     * 检查开始动画是否需要重试
     */
    private boolean checkReCountEnterAnim(@NonNull BaseItemData data, int idleIndex) {
        if( idleIndex >= 0 ) {
            mEnterAnimIdleIndexReCount = 0;
            return false;
        }
        /* 当空闲下标为-1时，发起重试处理（间隔1.5秒），超过3次则取消重试 */
        mEnterAnimIdleIndexReCount++;
        postDelayed(() -> {
            if( mEnterAnimIdleIndexReCount <= 0 ) return;
            if( mEnterAnimIdleIndexReCount <= 3 ) {
                enterAnim( data );
            }else {
                mEnterAnimIdleIndexReCount = 0;
            }
            log(
                    "checkReCountEnterAnim.postDelayed -> idleIndex:%s, reCount=%s",
                    idleIndex, mEnterAnimIdleIndexReCount
            );
        }, 1500);
        log(
                "checkReCountEnterAnim -> idleIndex:%s, reCount=%s, data:%s",
                idleIndex, mEnterAnimIdleIndexReCount, data
        );
        return true;
    }

    private boolean checkBindByCheckExitAnim(String tag, int ribbonMsgId, ViewHolder holder) {
        //存在等待展示的数据
        BaseItemData changedData = mAdapter.findWaitingChangedData( ribbonMsgId );
        log( "ribbonAnim -> waitAnim -> " +
                        "tag:%s, idleLine:%s, maxLine:%s, ribbonMsgId:%s, changedData:%s",
                tag, getCurrentIdleLineCount(), mMaxLine,
                changedData == null ? 0 : changedData.getRibbonMsgId(), changedData
        );
        if( changedData != null ) {
            //绑定ViewHolder
            getAdapter().bindViewHolder( holder, changedData, getLineIndex() );
            return true;
        }
        return false;
    }
    private OnRibbonCallable<Adapter<? extends BaseItemData, ? extends ViewHolder>, Integer> mOnDataUpdateCall;
    private final Map<Integer, RunSpinExitAnim> mRunSpinExitAnimMap = new HashMap<>();

    public class RunSpinExitAnim implements Runnable {
        private final WeakReference<ViewHolder> wrHolder;
        private BaseItemData data;
        private long waitMillisecond;
        private Runnable waitRunnable;

        public RunSpinExitAnim(ViewHolder holder) {
            wrHolder = new WeakReference<>( holder );
        }

        public void update(BaseItemData data, long waitMillisecond) {
            this.data = data;
            this.waitMillisecond = waitMillisecond;
        }

        @Override
        public void run() {
            RunSpinExitAnim curThis = this;
            EnterRibbonView.Adapter<?, ?> adapter = getAdapter();
            ViewHolder holder = getHolder();
            BaseItemData data = getData();

            log( "ribbonAnim -> RunSpinExitAnim -> " +
                            "have adapter:%s, have holder:%s, have data:%s",
                    adapter != null, holder != null, data != null
            );
            if( adapter == null || holder == null || data == null ) return;
            //彩带消息id
            int id = data.getRibbonMsgId();
            //检查是否需要重新绑定
            boolean checkBind = checkBindByCheckExitAnim( "runSpinExitAnim", id, holder );
            //如果存在绑定则跳过，等待下一次检查
            if( checkBind ) {
                //自旋
                waitRunnable = () -> {
                    //移除等待的数据
                    adapter.removeWaitingChangedData( id );
                    curThis.run();
                };
                postDelayed( waitRunnable, getWaitMillisecond() );
                //检查全部退出动画的空闲状态（防止卡死不退出）
                checkExitAnimIdleState( this );
                return;
            }
            //结束动画
            exitAnim( this );
        }

        public void removeSpin() {
            if( waitRunnable != null ) removeCallbacks( waitRunnable );
            removeCallbacks( this );
        }

        @Nullable
        public ViewHolder getHolder() { return wrHolder.get(); }
        public BaseItemData getData() { return data; }
        public long getWaitMillisecond() { return waitMillisecond; }
    }

    private void updateRunSpinExitAnim(int ribbonMsgId, RunSpinExitAnim run,
                                       @NonNull BaseItemData data, long waitMillisecond) {
        //初始化自旋退出动画
        run.update( data, waitMillisecond );
        //更新
        mRunSpinExitAnimMap.put( ribbonMsgId, run );
    }
    private void checkExitAnim(ViewHolder holder, @NonNull BaseItemData data, long waitMillisecond) {
        int id = data.getRibbonMsgId();
        //获取正在处理的自旋退出动画
        RunSpinExitAnim run = findRunSpinExitAnim( id );
        //实例一个自旋退出动画
        if( run == null ) run = new RunSpinExitAnim( holder );
        //移除掉之前的自旋
        run.removeSpin();
        //更新Running
        updateRunSpinExitAnim( id, run, data, waitMillisecond );
        //动态监听数据源发生变更
        if( mOnDataUpdateCall == null ) {
            mOnDataUpdateCall = (adapter, ribbonMsgId) -> {
                RunSpinExitAnim updateRun = findRunSpinExitAnim( ribbonMsgId );
                if( updateRun == null ) return;
                //获取当前自旋退出动画持有的Holder
                ViewHolder rHolder = updateRun.getHolder();
                if( rHolder == null ) return;
                BaseItemData findData = mAdapter.findWaitingChangedData( ribbonMsgId );
                if( findData == null ) findData = updateRun.getData();
                if( findData == null ) return;
                //更新Running
                updateRunSpinExitAnim(
                        ribbonMsgId, updateRun, findData, updateRun.getWaitMillisecond()
                );
                //检查是否需要重新绑定
                if( checkBindByCheckExitAnim( "dataUpdate", ribbonMsgId, rHolder ) ) {
                    //存在数据时，重新检查
                    checkExitAnim( rHolder, findData, updateRun.getWaitMillisecond() );
                }
            };
            getAdapter().setOnDataUpdateCall( mOnDataUpdateCall );
        }
        //等待时
        ViewHolder rHolder = run.getHolder();
        if( rHolder == null ) return;
        rHolder.onWaitAnimation( rHolder, data, waitMillisecond );
        if( mOnViewAnimationListener != null ) {
            mOnViewAnimationListener.onWaitAnimation( rHolder, data, waitMillisecond );
        }
        //发起自旋退出动画
        postDelayed( run, waitMillisecond );
        log( "ribbonAnim -> checkExitAnim -> " +
                        "runHash:%s, holderHash:%s, rHolderHash:%s, waitMillisecond:%s, data:%s",
                run.hashCode(), holder.hashCode(), rHolder.hashCode(), waitMillisecond, data
        );
    }

    @Nullable
    private RunSpinExitAnim findRunSpinExitAnim(int id) {
        return mRunSpinExitAnimMap.containsKey( id ) ? mRunSpinExitAnimMap.get( id ) : null;
    }

    @UiThread
    private void exitAnim(RunSpinExitAnim runSpinExitAnim) {
        if( isRelease || runSpinExitAnim == null ) return;
        ViewHolder holder = runSpinExitAnim.getHolder();
        BaseItemData data = runSpinExitAnim.getData();
        if( holder == null || data == null ) return;
        //获取初始化的动画配置
        CreateQueue animInit = initAnimation( holder, data, false );
        ValueAnimator valAnim = updateAnim(
                2,
                holder.getExitValAnim(),
                animInit.getValues(),
                mExitMillisecond,
                animInit.getTimeInterpolator(),
                ( view, time ) -> holder.onExitAnimationUpdate( holder, data, mItemOffsetX, time ),
                ( view, anim ) -> {
                    //彩带消息id
                    int id = data.getRibbonMsgId();
                    holder.getItemView().setVisibility( INVISIBLE );
                    //销毁ViewHolder
                    getAdapter().destroyHolder( holder, data, getLineIndex() );
                    //移除掉当前自旋退出动画
                    mRunSpinExitAnimMap.remove( id );
                    //移除监听数据源发生变更
                    mOnDataUpdateCall = null;
                    //释放一个空闲行数
                    if( getCurrentIdleLineCount() < mMaxLine ) {
                        mAtoIdleLineCount.incrementAndGet();
                    }
                    //移除id
                    getAdapter().removeDataId( id );
                    //解除空闲状态
                    updateIdleState( holder, -1 );
                    /* 清空当前数据 */
                    mCurrentHolder = null;
                    mCurrentData = null;
                    //处理下一个动画
                    nextQueueAnim( false );
                    log( "ribbonAnim -> exitAnim end" );
                } );
        holder.setExitValAnim( valAnim );
    }

    /**
     处理动画
     @param type                处理类型。1:开始动画, 2:退出动画
     @param valAnim             值动画
     @param values              值动画区间
     @param dur                 动画时长
     @param timeInterpolator    插值器
     @param callUpdate          值被更新时
     @param callEnd             动画结束时
     @return                    处理好的值动画
     */
    @NonNull
    private ValueAnimator updateAnim(
            int type,
            @Nullable ValueAnimator valAnim,
            float[] values, long dur,
            @Nullable TimeInterpolator timeInterpolator,
            @NonNull OnRibbonCallable<EnterRibbonView, Float> callUpdate,
            @NonNull OnRibbonCallable<EnterRibbonView, ValueAnimator> callEnd) {

        if( valAnim == null ) {
            valAnim = new ValueAnimator();
            if( timeInterpolator != null ) valAnim.setInterpolator( timeInterpolator );
        }
        valAnim.setFloatValues( values );
        valAnim.setDuration( dur );
        valAnim.addUpdateListener( anim ->
                callUpdate.call( this, (float) anim.getAnimatedValue() )
        );
        valAnim.addListener( new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) { }
            @Override
            public void onAnimationEnd(Animator animation) {
                ValueAnimator valAnim = (ValueAnimator) animation;
                //移除动画监听器
                valAnim.removeAllUpdateListeners();
                valAnim.removeAllListeners();
                callEnd.call( EnterRibbonView.this, valAnim );
            }
            @Override
            public void onAnimationCancel(Animator animation) { }
            @Override
            public void onAnimationRepeat(Animator animation) { }
        } );
        ValueAnimator fValAnim = valAnim;
        Runnable start = fValAnim::start;
        //type = 1:进入动画, 2:退出动画
        long delayed = type == 1 ? mAnimationEnterDelayedTime : mAnimationExitDelayedTime;
        if ( delayed > 0 ) {
            postDelayed( start, delayed );
        } else {
            start.run();
        }
        return valAnim;
    }

    /**
     通过空闲下标获取空闲的Holder
     @param data            数据源
     @param idleIndex       空闲的下标
     @return                空闲的Holder
     */
    @NonNull
    private ViewHolder getIdleHolder(@NonNull BaseItemData data, int idleIndex) {
        ViewHolder holder = mRibbonQueueDataPool.get( idleIndex );
        int lineIndex = getLineIndex();
        //绑定ViewHolder
        getAdapter().bindViewHolder( holder, data, lineIndex );
        //设置当前展示的列下标
        holder.setCurrentLineIndex( lineIndex );
        log("getHolder -> idleIndex:%s, lineIndex=%s, data:%s", idleIndex, lineIndex, data );
        return holder;
    }

    private int getLineIndex() { return mAtoNextLineIndex.get() % mMaxLine; }

    /**
     获取空闲下标
     当没有空闲的下标时会处于自旋状态，直到空闲出下标为止
     @return    下标
     */
    private int getIdleIndex(int itemViewType) {
        try {
            int index = 0;
            long currentTime = currentTimeMillis();
            while( true ) {
                //重置下标
                if( index >= mRibbonQueueDataPool.size() ) index = 0;
                ViewHolder vh = mRibbonQueueDataPool.get( index );
                if( vh == null ) continue;
                if( vh.isIdle() && vh.getItemViewType() == itemViewType ) return index;
                //2秒自旋，如果还是没找到则返回
                if( currentTimeMillis() - currentTime >= 2000 ) return -1;
                index++;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return -1;
    }

    /**
     更新彩带的空闲状态
     @param holder          持有的Holder
     @param idleIndex       空闲下标，-1时为空闲状态
     */
    private void updateIdleState(ViewHolder holder, int idleIndex) {
        if( isRelease ) return;
        holder.setIdleIndex( idleIndex );
        if( idleIndex <= -1 ) {
            //清空空闲时间戳
            holder.setIdleStateCreateTimestamp( 0 );
            return;
        }
        View itemView = holder.getItemView();
        //设置高宽（自动测量）
        holder.setSizeByAuto( itemView, holder.getWidth(), holder.getHeight() );
        //创建空闲时间戳
        holder.setIdleStateCreateTimestamp( currentTimeMillis() );
    }

    /**
     * 检查退出动画的空闲状态
     */
    private void checkExitAnimIdleState(RunSpinExitAnim runAnim) {
        if( runAnim == null ) return;
        for (ViewHolder queueHolder : mRibbonQueueDataPool) {
            long createTime = queueHolder.getIdleStateCreateTimestamp();
            if( createTime == 0 ) continue;
            long fullTime = createTime +
                    mEnterMillisecond + runAnim.getWaitMillisecond() + mExitMillisecond;
            long curTime = currentTimeMillis();
            if( fullTime > curTime ) {
                exitAnim( runAnim );
                log( "checkExitAnimIdleState -> exit " +
                                "curTime:%s, fullTime:%s, diff:%s",
                        curTime, fullTime, fullTime - curTime
                );
            }
            log( "checkExitAnimIdleState -> normal " +
                            "curTime:%s, fullTime:%s, diff:%s",
                    curTime, fullTime, fullTime - curTime
            );
        }
    }

    private long currentTimeMillis() { return System.currentTimeMillis(); }

    public static class BaseItemData implements IItemData {
        private final int ribbonMsgId;

        public BaseItemData() {
            this( (int) ( ( Math.random() * 100000D ) + 899999D ) );
        }

        public BaseItemData(int ribbonMsgId) {
            this.ribbonMsgId = Math.abs( ribbonMsgId );
        }

        public int getRibbonMsgId() { return ribbonMsgId; }

        public int onItemViewType() { return 0; }
    }

    public abstract static class Adapter<E extends BaseItemData, VH extends ViewHolder> {
        public interface OnItemClickListener<E extends BaseItemData> {
            void onItemClick(View view, E data, int lineIndex);
        }
        //这里初始化数量遵循 PriorityQueue.DEFAULT_INITIAL_CAPACITY 的默认值
        private final Queue<E> mDataQueue;
        private final List<Integer> mDataIds;
        private final Map<Integer, E> mWaitingChangedDataMap;
        private WeakReference<EnterRibbonView> mWrParentView;
        private OnItemClickListener<E> mOnItemClickListener;
        private OnRibbonCallable<? super Adapter<?, ?>, Integer> mOnDataUpdateCall;

        public Adapter() { this( null ); }
        public Adapter(Comparator<E> comparator) {
            mDataIds = new LinkedList<>();
            mWaitingChangedDataMap = new HashMap<>();
            if( comparator == null ) {
                mDataQueue = new LinkedBlockingQueue<>();
                return;
            }
            mDataQueue = new PriorityBlockingQueue<>( 11, comparator );
        }

        private void setOnDataUpdateCall(OnRibbonCallable<? super Adapter<?, ?>, Integer> call) {
            mOnDataUpdateCall = call;
        }

        void setEnterRibbonView(EnterRibbonView rootView) {
            mWrParentView = new WeakReference<>( rootView );
        }

        @Nullable
        public EnterRibbonView getParentView() {
            if( mWrParentView == null || mWrParentView.get() == null ) return null;
            return mWrParentView.get();
        }

        @NonNull
        public abstract VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType);

        public abstract void onBindViewHolder(@NonNull VH holder, E data, int lineIndex);

        public abstract void onDestroyHolder(@NonNull VH holder, E data, int lineIndex);

        public abstract int onViewTypeCount();

        @NonNull
        public final VH createViewHolder(@NonNull ViewGroup parent, int viewType) {
            VH holder = onCreateViewHolder( parent, viewType );
            holder.setItemViewType( viewType );
            return holder;
        }
        @SuppressWarnings("unchecked")
        private void bindViewHolder(@NonNull ViewHolder holder,
                                    @NonNull BaseItemData data, int lineIndex) {
            E eData = (E) data;
            holder.getItemView().setOnClickListener( v -> {
                if( mOnItemClickListener == null ) return;
                mOnItemClickListener.onItemClick( v, eData, lineIndex );
            } );
            onBindViewHolder( (VH) holder, eData, lineIndex );
        }

        @SuppressWarnings("unchecked")
        private void destroyHolder(@NonNull ViewHolder holder,
                                   @NonNull BaseItemData data, int lineIndex) {
            E eData = (E) data;
            holder.getItemView().setOnClickListener( null );
            onDestroyHolder( (VH) holder, eData, lineIndex );
        }

        public int getItemViewType(int initIndex, int viewTypeIndex) { return 0; }

        public void onRelease() {
            clear();
            if( mWrParentView != null ) mWrParentView.clear();
            mOnItemClickListener = null;
        }

        public void setOnItemClickListener(OnItemClickListener<E> listener) {
            mOnItemClickListener = listener;
        }

        public void notifyDataPoll() {
            EnterRibbonView rootView = getParentView();
            if( rootView != null ) rootView.post( rootView::notifyNext );
        }

        @SafeVarargs
        public final boolean addItemData(E... data) {
            if( data == null ) return false;
            if( data.length == 1 ) {
                mDataIds.add( data[ 0 ].getRibbonMsgId() );
                return mDataQueue.add( data[ 0 ] );
            }

            return addItemDataAll( Arrays.asList( data ) );
        }

        public boolean addItemDataAll(Collection<? extends E> c) {
            for( E e : c ) {
                if( e != null ) mDataIds.add( e.getRibbonMsgId() );
            }
            return mDataQueue.addAll( c );
        }

        public boolean hasDataRibbonMsgId(int ribbonMsgId) {
            return mDataIds.contains( ribbonMsgId );
        }
        public boolean updateItemData(int ribbonMsgId, E data) {
            if( data == null ) return false;
            mWaitingChangedDataMap.put( ribbonMsgId, data );
            if( mOnDataUpdateCall != null ) mOnDataUpdateCall.call( this, ribbonMsgId );
            return true;
        }

        public boolean removeItemData(E data) {
            removeDataId( data.getRibbonMsgId() );
            return mDataQueue.remove( data );
        }
        public boolean removeItemDataAll(Collection<E> c) {
            for( E e : c ) {
                if( e != null ) removeDataId( e.getRibbonMsgId() );
            }
            return mDataQueue.removeAll( c );
        }

        public int getItemCount() { return mDataQueue.size(); }
        public void clear() {
            mDataIds.clear();
            mDataQueue.clear();
        }

        public E findWaitingChangedData(int ribbonMsgId) {
            if( !mWaitingChangedDataMap.containsKey( ribbonMsgId ) ) return null;
            return mWaitingChangedDataMap.get( ribbonMsgId );
        }
        private void removeWaitingChangedData(int ribbonMsgId) {
            if( !mWaitingChangedDataMap.containsKey( ribbonMsgId ) ) return;
            mWaitingChangedDataMap.remove( ribbonMsgId );
        }
        private E poll() { return mDataQueue.poll(); }

        private void removeDataId(int id) {
            mDataIds.remove( Integer.valueOf( id ) );
        }
    }

    /**
     动画监听器
     */
    public interface OnViewAnimationListener {
        /**
         进入动画 - 初始化
         @param itemOffsetX             Item偏移的X值
         @param multiLineDirection      多列时的展示方向，1：从上往下，-1：从下往上
         @return                        用于初始化动画
         */
        @Nullable
        CreateQueue onEnterCreateQueue(ViewHolder holder, @NonNull BaseItemData data,
                                       float itemOffsetX, int multiLineDirection);

        /**
         进入动画 - 值更新
         @param val                     更新的值
         */
        void onEnterAnimationUpdate(ViewHolder holder, @NonNull BaseItemData data, float val);

        /**
         等待动画 - 实际无动画，仅返回等待的时间
         @param waitMillisecond         等待的时间
         */
        void onWaitAnimation(ViewHolder holder, @NonNull BaseItemData data, long waitMillisecond);

        /**
         退出动画 - 初始化
         @param itemOffsetX             Item偏移的X值
         @param multiLineDirection      多列时的展示方向，1：从上往下，-1：从下往上
         @return                        用于初始化动画
         */
        @Nullable
        CreateQueue onExitCreateQueue(ViewHolder holder, @NonNull BaseItemData data,
                                      float itemOffsetX, int multiLineDirection);

        /**
         退出动画 - 值更新
         @param val                     更新的值
         */
        void onExitAnimationUpdate(ViewHolder holder, @NonNull BaseItemData data, float itemOffsetX, float val);
    }

    public static class OnViewAnimationAdapter implements OnViewAnimationListener {

        @Nullable
        @Override
        public CreateQueue onEnterCreateQueue(ViewHolder holder, @NonNull BaseItemData data,
                                              float itemOffsetX, int multiLineDirection) {
            return null;
        }

        @Override
        public void onEnterAnimationUpdate(ViewHolder holder, @NonNull BaseItemData data, float val) {

        }

        @Override
        public void onWaitAnimation(ViewHolder holder, @NonNull BaseItemData data, long waitMillisecond) {

        }

        @Nullable
        @Override
        public CreateQueue onExitCreateQueue(ViewHolder holder, @NonNull BaseItemData data,
                                             float itemOffsetX, int multiLineDirection) {
            return null;
        }

        @Override
        public void onExitAnimationUpdate(ViewHolder holder, @NonNull BaseItemData data, float itemOffsetX, float val) {

        }
    }

    public abstract static class ViewHolder implements OnViewAnimationListener {
        @NonNull
        private final ViewGroup parentView;
        @NonNull
        private final View itemView;
        private int width;
        private int height;
        private int idleIndex = -1;                //空闲下标为-1时为空闲状态，>=0时为池中的下标
        private long idleStateCreateTimestamp;     //空闲状态创建时间
        private int currentLineIndex = -1;         //当前展示的列
        private ValueAnimator enterValAnim;        //进入动画
        private ValueAnimator exitValAnim;         //退出动画
        private int itemViewType;                  //Item的布局类型
        private int horizontalMargins;             //Item的水平间距
        private int verticalMargins;               //Item的垂直间距

        public ViewHolder(@NonNull ViewGroup parent, @NonNull View itemView, int width, int height) {
            parentView = parent;
            this.itemView = itemView;
            //设置高宽（自动测量）
            setSizeByAuto( itemView, width, height );
        }
        public ViewHolder(@NonNull ViewGroup parent, @NonNull View itemView) {
            this( parent, itemView, 0, 0 ); // 高宽 < 0 时自动获取
        }
        public ViewHolder(@NonNull ViewGroup parent, @LayoutRes int layoutResId, int width, int height) {
            parentView = parent;
            itemView = LayoutInflater.from( parent.getContext() )
                    .inflate( layoutResId, parent, false );
            //设置高宽（自动测量）
            setSizeByAuto( itemView, width, height );
        }
        public ViewHolder(@NonNull ViewGroup parent, @LayoutRes int layoutResId) {
            this(
                    parent, layoutResId,
                    ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT
            );
        }

        @NonNull
        @Override
        public String toString() {
            return "ViewHolder{" +
                    "itemView=" + itemView +
                    ", width=" + width +
                    ", height=" + height +
                    ", idleIndex=" + idleIndex +
                    ", idleStateCreateTimestamp=" + idleStateCreateTimestamp +
                    ", currentLineIndex=" + currentLineIndex +
                    ", enterValAnim=" + enterValAnim +
                    ", exitValAnim=" + exitValAnim +
                    ", itemViewType=" + itemViewType +
                    ", horizontalMargins=" + horizontalMargins +
                    ", verticalMargins=" + verticalMargins +
                    '}';
        }

        @NonNull
        public Context getContext() { return itemView.getContext(); }


        /**
         Holder在释放资源前会调用该类
         */
        public void onReleaseHolder(@NonNull ViewGroup parent) {
            try {
                parent.removeView( itemView );
            }catch(Exception e) {
                e.printStackTrace();
            }
            try {
                clearAnim( enterValAnim );
            }catch(Exception e) {
                e.printStackTrace();
            } finally {
                enterValAnim = null;
            }
            try {
                clearAnim( exitValAnim );
            }catch(Exception e) {
                e.printStackTrace();
            } finally {
                exitValAnim = null;
            }
        }

        private void clearAnim(ValueAnimator valAnim) {
            valAnim.cancel();
            valAnim.removeAllUpdateListeners();
            valAnim.removeAllListeners();
        }

        @NonNull
        public View getItemView() { return itemView; }

        private void setSizeByAuto(View itemView, int width, int height) {
            //获取 Item 高度和宽度
            if( Math.min( width, height ) <= 0 ) {
                width = itemView.getWidth();
                height = itemView.getHeight();
            }
            //获取 Item measure 测量高度和宽度（获取失败时）
            if( Math.min( width, height ) <= 0 ) {
                itemView.measure( 0, 0 );
                width = itemView.getMeasuredWidth();
                height = itemView.getMeasuredHeight();
            }
            //获取 Item LayoutParams 高度和宽度（获取和测量都失败时）
            if( Math.min( width, height ) <= 0 ) {
                ViewGroup.LayoutParams lp = itemView.getLayoutParams();
                width = lp == null ? 0 : lp.width;
                height = lp == null ? 0 : lp.height;
            }
            //默认的Item高度和宽度（均获取失败时）
            if( Math.min( width, height ) <= 0 ) {
                width = ViewGroup.LayoutParams.WRAP_CONTENT;
                height = ViewGroup.LayoutParams.WRAP_CONTENT;
            }
            this.width = width;
            this.height = height;
        }
        public int getWidth() { return width; }
        public int getHeight() { return height; }
        public int getFullWidth() { return getWidth() + getHorizontalMargins(); }
        public int getFullHeight() { return getHeight() + getVerticalMargins(); }
        public int getParentWidth() { return parentView.getMeasuredWidth(); }
        public int getParentHeight() { return parentView.getMeasuredHeight(); }

        boolean isIdle() { return getIdleIndex() == -1; }
        int getIdleIndex() { return idleIndex; }
        void setIdleIndex(int index) { idleIndex = index; }

        public long getIdleStateCreateTimestamp() { return idleStateCreateTimestamp; }
        public void setIdleStateCreateTimestamp(long timestamp) {
            idleStateCreateTimestamp = timestamp;
        }

        public int getCurrentLineIndex() { return currentLineIndex; }
        void setCurrentLineIndex(int lineIndex) { currentLineIndex = lineIndex; }

        ValueAnimator getEnterValAnim() { return enterValAnim; }
        void setEnterValAnim(ValueAnimator enterValAnim) { this.enterValAnim = enterValAnim; }

        ValueAnimator getExitValAnim() { return exitValAnim; }
        void setExitValAnim(ValueAnimator exitValAnim) { this.exitValAnim = exitValAnim; }

        public int getItemViewType() { return itemViewType; }
        void setItemViewType(int type) { itemViewType = type; }

        public int getHorizontalMargins() { return horizontalMargins; }
        public void setHorizontalMargins(int margins) { horizontalMargins = margins; }

        public int getVerticalMargins() { return verticalMargins; }
        public void setVerticalMargins(int margins) { verticalMargins = margins; }
    }

    public static class CreateQueue {
        private float[] values;
        private TimeInterpolator timeInterpolator;

        private CreateQueue() {}
        private CreateQueue(TimeInterpolator timeInterpolator, float... values) {
            this.values = values;
            this.timeInterpolator = timeInterpolator;
        }

        public static CreateQueue create(@Nullable TimeInterpolator timeInterpolator,
                                         float... values) {
            return new CreateQueue( timeInterpolator, values );
        }
        public static CreateQueue create(float... values) {
            return create( new DecelerateInterpolator(), values );
        }

        public float[] getValues() { return values; }
        public void setValues(float[] values) { this.values = values; }

        public TimeInterpolator getTimeInterpolator() { return timeInterpolator; }
    }
}